##############
# (*) Kaleidoscope
# By: Allan Spale
# August 2008

from __future__ import with_statement
import uuid
import types

from Kaleidoscope.KSColumn import KSColumn
from Kaleidoscope.KSEventManager import KSEventManager
from Kaleidoscope.KSTransaction import KSTransaction
from Kaleidoscope.KSUtility import KSUtility

class KSColumnVirtualError(Exception):
    """
    Exception class for KSColumnVirtual

    """
    B_UNKNOWN_EXCEPTION = True
    B_CLASS_EXCEPTION = False

    def __init__(self, u_message):
        self.__u_message = u_message

    def __str__(self):
        return repr(self.__u_message)

    @staticmethod
    def print_and_raise_error(u_message, b_unknown_exception):
        """
        Convenience method for printing and raising an error for this
        particular Exception subclass.

        """
        #sys.stderr.write(sys.exc_info())  # write initial error
        u_message = u'KSColumnVirtual.py: ' + u_message

        if (b_unknown_exception):
            raise Exception(u_message)
        else:
            raise KSColumnVirtualError(u_message)


class KSColumnVirtual(KSColumn):
    """
    In order to handle various primitive types that would be pointless to
    store into a database (e.g. every integer in a 32-bit or 64-bit address
    space), a column must exist that simulates the existence of this data.
    This column will be read-only because the bit patterns that are
    interpreted by this type are already externally defined and well-defined.
    To maintain the hash table metaphor, the key and the value will be
    identical. Its disk-location will be indicated by an asterisk string (*).
    The type will be decoded on the receiving end. For this reason, this class
    will merely be a pass-through to simulate the case of this column's
    existence.

    """
    @property
    def is_open(self):
        return self.__b_open

    def __init__(self, u_type):
        """
        There is no need to initialize the parent class. Just use the
        parent class for various methods. The only permitted virtual types
        are: int, float, long, and unicode. ASCII string type is not allowed.

        """
        try:
            self.__b_open = False

            self.__id = uuid.uuid4()
            self.__ksem_event_manager = KSEventManager(self.__id)
            self.__context = KSUtility(
                self.__ksem_event_manager, None)
            self.__ksem_event_manager.register_all_functions_from_class(
                KSColumnVirtual, 600)

            if (u_type == u'int'):
                self.__t_type = types.IntType
            elif (u_type == u'float'):
                self.__t_type = types.FloatType
            elif (u_type == u'long'):
                self.__t_type = types.LongType
            elif (u_type == u'unicode'):
                self.__t_type = types.UnicodeType
            else:
                KSColumnVirtualError.print_and_raise_error(u'The only types '
                    + u'that are allowed for virtual columns are: int, float, '
                    + u'long, and unicode.',
                    KSColumnVirtualError.B_CLASS_EXCEPTION)

        except Exception:
            KSColumnVirtualError.print_and_raise_error(u'The value could not '
                + u'be returned due to an unknown error.',
                KSColumnVirtualError.B_UNKNOWN_EXCEPTION)



    def open(self, i_flags, i_permissions, kstx_parent_transaction):
        """
        Since no column physically exists, the column does not need to
        be opened or closed.

        """
        pass


    def close(self):
        """
        Since no column physically exists, the column does not need to
        be opened or closed.

        """
        pass


    def get_data_from_key(self, obj_key, kstx_parent_transaction,
                              b_return_packed_data):
        """
        Loopback function...whatever the type and data is, return it...
        and pickle it if necessary.

        """
        try:
            with self.__context.type_check_and_send_event(
                self.get_data_from_key, [self.__t_type, KSTransaction,
                bool], [obj_key, kstx_parent_transaction,
                b_return_packed_data]) as kstx_transaction:

                if (b_return_packed_data):
                    obj_data = self.pickle_data(obj_key)
                else:
                    obj_data = obj_key

                kstx_parent_transaction.commit(0, self.get_data_from_key)

            self.__ksem_event_manager.send_after_event_message(
                self.get_data_from_key, [obj_data])
            return obj_data

        except Exception:
            kstx_parent_transaction.cancel()
            KSColumnVirtualError.print_and_raise_error(u'The value could not '
                + u'be returned due to an unknown error.',
                KSColumnVirtualError.B_UNKNOWN_EXCEPTION)


    def get_keys_from_data_value(self, obj_data, kstx_parent_transaction,
                                     b_return_packed_data):
        """
        Loopback function...whatever the type and data is, return it...
        and pickle it if necessary. Because it is not clear what type is
        being returned, prohibit the return of packed data (which one would
        think is a packed key) in this case.

        """
        try:
            with self.__context.type_check_and_send_event(
                self.get_keys_from_data_value, [self.__t_type, KSTransaction,
                bool], [obj_data, kstx_parent_transaction,
                b_return_packed_data]) as kstx_transaction:

                if (b_return_packed_data):
                    KSColumnVirtualError.print_and_raise_error(u'Packed keys '
                        + u'from a virtual column are not permitted.',
                        KSColumnVirtualError.B_CLASS_EXCEPTION)
                else:
                    list_data = [obj_data]
                kstx_parent_transaction.commit(0, self.get_keys_from_data_value)

            self.__ksem_event_manager.send_after_event_message(
                self.get_keys_from_data_value, [list_data])
            return list_data

        except Exception:
            kstx_parent_transaction.cancel()
            KSColumnVirtualError.print_and_raise_error(u'The key could not '
                + u'be returned due to an unknown error.',
                KSColumnVirtualError.B_UNKNOWN_EXCEPTION)


    def has_key(self, obj_key, kstx_parent_transaction):
        """
        Rather useless comparison, but it may serve some purpose in a
        very remotely odd case.

        """
        try:
            with self.__context.type_check_and_send_event(
                self.has_key, [self.__t_type, KSTransaction],
                [obj_key, kstx_parent_transaction]) as kstx_transaction:

                result = (obj_key == obj_key)

            kstx_parent_transaction.commit(0, self.has_key)

            self.__ksem_event_manager.send_after_event_message(
                self.has_key, [result])
            return result

        except Exception:
            kstx_parent_transaction.cancel()
            KSColumnVirtualError.print_and_raise_error(u'Because of an '
                + u'internal error, the column could not be checked for '
                + u'the key.', KSColumnVirtualError.B_UNKNOWN_EXCEPTION)


    def has_value(self, obj_value, kstx_parent_transaction):
        """
        Rather useless comparison, but it may serve some purpose in a
        very remotely odd case.

        """
        try:
            with self.__context.type_check_and_send_event(
                self.has_value, [self.__t_type, KSTransaction],
                [obj_value, kstx_parent_transaction]) as kstx_transaction:

                result = (obj_value == obj_value)

            kstx_parent_transaction.commit(0, self.has_value)

            self.__ksem_event_manager.send_after_event_message(
                self.has_value, [result])
            return result

        except Exception:
            kstx_parent_transaction.cancel()
            KSColumnVirtualError.print_and_raise_error(u'Because of an '
                + u'internal error, the column could not be checked for '
                + u'the value.', KSColumnVirtualError.B_UNKNOWN_EXCEPTION)


    def reorganize_keys(self, kstx_parent_transaction):
        """
        No keys can be reorganized because the column data is fixed.

        """
        pass


    def factory_function(self, u_type):
        """
        Factory function used to reconstruct the column.

        """
        return KSColumnVirtual(u_type)


    def generate_id(self, kstx_parent_transaction):
        """
        Not allowed

        """
        raise NotImplementedError, u'generate_id ' \
            + u'is not permitted for a virtual column.'


    def change_datetimes(self, mx_start_datetime, mx_last_datetime):
        """
        Not allowed

        """
        raise NotImplementedError, u'change_datetimes ' \
            + u'is not permitted for a virtual column.'